package com.gitee.wsl.mathematics.function.random

import com.gitee.wsl.math.random.nextFloat
import kotlin.math.ln
import kotlin.math.sqrt
import kotlin.random.Random


class KissRandom(seed: Int) : Random() {
    private var x = seed
    private var y = 362436000
    private var z = 521288629
    private var c = 7654321

    private var hasNextGaussian = false
    private var nextGaussian = 0f

    /**
     * 32-bit KISS RNG
     * https://de.wikipedia.org/wiki/KISS_(Zufallszahlengenerator)
     *
     * Slower than the default Random implementation (on JVM slightly, Javascript drastically) but is
     * supposed to have better statistical properties.
     */
    override fun nextBits(bitCount: Int): Int {
        // linear congruential generator
        x = 69069 * x + 12345

        // xorshift
        y = y xor (y shl 13)
        y = y xor (y ushr 17)
        y = y xor (y shl 5)

        // multiply with carry
        val t = 698769069L * z + c
        c = (t ushr 32).toInt()
        z = t.toInt()

        val r = x + y + z
        return if (bitCount == 32) r else r ushr (32 - bitCount)
    }

    fun randomGaussianF(): Float {
        if (hasNextGaussian) {
            hasNextGaussian = false
            return nextGaussian
        }

        var x1: Float
        var x2: Float
        var w: Float
        do {
            x1 = nextFloat(-1f, 1f)
            x2 = nextFloat(-1f, 1f)
            w = x1 * x1 + x2 * x2
        } while (w >= 1f || w == 0f)
        w = sqrt(-2 * ln(w) / w)

        nextGaussian = x2 * w
        hasNextGaussian = true
        return x1 * w
    }
}
